// -*- coding: utf-8 -*-

#ifdef __cplusplus

#ifndef __HYBRID_EXPR_VISITOR__
#define __HYBRID_EXPR_VISITOR__

#include <iostream>
#include <string>

#include <boost/shared_ptr.hpp>
#include <boost/bind.hpp>

#include "elisa/elisa_constraint.h"
#include "elisa/elisa_constant.h"
#include "elisa/elisa_real_expr.h"
//#include "elisa/hybrid/hybrid.h"
#include "hybrid_parser.h"

namespace elisa 
{

#define VISIT_PTR_GEN(T)    boost::shared_ptr<T>
//#define VISIT_PTR_GEN(T)    

#define VISIT_UN_OP(Op)		HybridExprVisitor<T> vst(*this);	\
	vst.visit(*e.child());										\
	m_value = VISIT_PTR_GEN(T)(new T(Op(*vst.value())));

#define VISIT_BIN_OP(Op)	HybridExprVisitor<T> vstL(*this), vstR(*this); \
	vstL.visit(*e.left()); vstR.visit(*e.right());						   \
	m_value = VISIT_PTR_GEN(T)(new T(Op(*vstL.value(), *vstR.value())));


template<typename T>
class HybridExprVisitor : public RealExprVisitor
{
	typedef boost::shared_ptr<T> TPtr;
	//typedef T *TPtr;

public:
	/// Constructor
	HybridExprVisitor(HybridParser& parser, 
					 int n, T *dVars, const T *vars, T t, void *param)
		: m_parser(parser), m_dVars(&dVars[0], &dVars[n]), m_vars(&vars[0], &vars[n])
		//, m_t(t) 
	{}

	HybridExprVisitor(HybridParser& parser, 
					 const std::vector<T>& vars, const T& t)
		: m_parser(parser), m_vars(vars), m_t(t) 
	{}

	HybridExprVisitor(HybridParser& parser, const std::vector<T>& vars)
		: m_parser(parser), m_vars(vars)
	{}

	HybridExprVisitor(const HybridExprVisitor& rhs)
		: m_parser(rhs.m_parser), m_dVars(rhs.m_dVars), m_vars(rhs.m_vars), m_t(rhs.m_t) 
	{}

	/// Destructor
	~HybridExprVisitor()	
	{}

	/// @name Base class's virtual methods
	//@{
	void visit(RealExprRep& e) {
		e.visit(*this);
	}

	void visited(RealExprRepConst& e);
	void visited(RealExprRepIntervalConst& e);

	void visited(RealExprRepVar& e) {
		std::string name(e.variable().getName());
//cout << "var[" << name << "]" << endl;

		if (m_parser.constants().find(name) != m_parser.constants().end()) {
			RealExpr e(*m_parser.constants()[name]);
			HybridExprVisitor<T> vst(*this);
			vst.visit(*e.rep());
			m_value = VISIT_PTR_GEN(T)(new T(*vst.value()));

		} else {
			int i(m_parser.toVarIndex(name));
			//m_value = boost::shared_ptr<T>(new T(m_vars[i]));
			//m_value = VISIT_PTR_GEN(T)(&m_vars[i]);
			m_value = VISIT_PTR_GEN(T)(new T(m_vars[i]));
		}
	}


	//T add(T& l, T&r) { return l + r; }
	void visited(RealExprRepAdd& e) {
		//VISIT_BIN_OP(add);
		VISIT_BIN_OP(operator+);
	}

	void visited(RealExprRepMul& e) {
		VISIT_BIN_OP(operator*);
	}

	void visited(RealExprRepSub& e) {
		VISIT_BIN_OP(operator-);
	}

	void visited(RealExprRepDiv& e) {
		VISIT_BIN_OP(operator/);
	}


	void visited(RealExprRepOpp& e) {
		VISIT_UN_OP(HybridExprVisitor<T>::oppF);
	}

	void visited(RealExprRepSqr& e) {
		VISIT_UN_OP(sqr);
	}

	void visited(RealExprRepSqrt& e) {
		VISIT_UN_OP(sqrt);
	}

	void visited(RealExprRepAbs& e) {
		//VISIT_UN_OP(HybridExprVisitor<T>::absF);
	}

	void visited(RealExprRepLog& e) {
		VISIT_UN_OP(log);
	}

	void visited(RealExprRepExp& e) {
		VISIT_UN_OP(exp);
	}

	void visited(RealExprRepCos& e) {
		VISIT_UN_OP(cos);
	}

	void visited(RealExprRepACos& e) {
		VISIT_UN_OP(acos);
	}

	void visited(RealExprRepSin& e) {
		VISIT_UN_OP(sin);
	}

	void visited(RealExprRepASin& e) {
		VISIT_UN_OP(asin);
	}

	void visited(RealExprRepTan& e) {
		VISIT_UN_OP(tan);
	}

	void visited(RealExprRepATan& e) {
		VISIT_UN_OP(atan);
	}

	void visited(RealExprRepCosh& e) {
		//VISIT_UN_OP(cosh);
	}

	void visited(RealExprRepACosh& e) {
		//VISIT_UN_OP(acosh);
	}

	void visited(RealExprRepSinh& e) {
		//VISIT_UN_OP(sinh);
	}

	void visited(RealExprRepASinh& e) {
		//VISIT_UN_OP(asinh);
	}

	void visited(RealExprRepTanh& e) {
		//VISIT_UN_OP(tanh);
	}

	void visited(RealExprRepATanh& e) {
		//VISIT_UN_OP(atanh);
	}

	void visited(RealExprRepPow& e) {
		VISIT_UN_OP(boost::bind(HybridExprVisitor<T>::powF, _1, e.exponent()));
	}


	void visited(RealExprRepMin& e) {
		// not supported
	}
	void visited(RealExprRepMax& e) {
		// not supported
	}
	void visited(RealExprRepSum& e) {
		// not supported
	}
	void visited(RealExprRepProd& e) {
		// not supported
	}
	//@}

	/// Returns the value for interval evaluation
	TPtr value() const
	{
		return m_value;
	}

private:
	static const T oppF(T& v) { return -v; }
	//static const T absF(T& v) { return abs(v); }
	static const T powF(T& v, int n) { return pow(v, n); }

	HybridParser& m_parser;
	std::vector<T> m_dVars;
	std::vector<T> m_vars;
	T m_t;

	TPtr m_value; // evaluator computed by the visitor
};


// visited(RealExprRepConst& e) の部分特殊化

template <> 
void HybridExprVisitor<VNInterval>::visited(RealExprRepConst& e) 
{
	Interval i(e.getInterval());
	m_value = VISIT_PTR_GEN(VNInterval)(new VNInterval(i.inf(), i.sup()));
};

template <> 
void HybridExprVisitor<AD_VNInterval>::visited(RealExprRepConst& e) 
{
	Interval i(e.getInterval());
	m_value = VISIT_PTR_GEN(AD_VNInterval)(
		new AD_VNInterval(VNInterval(i.inf(), i.sup())));
};

template <> 
void HybridExprVisitor<vnodelp::Tinterval>::visited(RealExprRepConst& e) 
{
	Interval i(e.getInterval());
	m_value = VISIT_PTR_GEN(vnodelp::Tinterval)(
		new vnodelp::Tinterval(VNInterval(i.inf(), i.sup())));
};

template <> 
void HybridExprVisitor<vnodelp::TFinterval>::visited(RealExprRepConst& e) 
{
	Interval i(e.getInterval());
	VNIntPtr vi(new VNInterval(i.inf(), i.sup()));
	m_value = VISIT_PTR_GEN(vnodelp::TFinterval)(new vnodelp::TFinterval(*vi));
};

template <> 
void HybridExprVisitor<RealExpr>::visited(RealExprRepConst& e) 
{
	m_value = VISIT_PTR_GEN(RealExpr)(
		new RealExpr(IntervalConstant(e.getInterval())));
};


// visited(RealExprRepIntervalConst& e) の部分特殊化

template <> 
void HybridExprVisitor<VNInterval>::visited(RealExprRepIntervalConst& e) 
{
	Interval i(e.getInterval());
	m_value = VISIT_PTR_GEN(VNInterval)(new VNInterval(i.inf(), i.sup()));
};

template <> 
void HybridExprVisitor<AD_VNInterval>::visited(RealExprRepIntervalConst& e) 
{
	Interval i(e.getInterval());
	m_value = VISIT_PTR_GEN(AD_VNInterval)(
		new AD_VNInterval(VNInterval(i.inf(), i.sup())));
};

template <> 
void HybridExprVisitor<vnodelp::Tinterval>::visited(RealExprRepIntervalConst& e) 
{
	Interval i(e.getInterval());
	m_value = VISIT_PTR_GEN(vnodelp::Tinterval)(
		new vnodelp::Tinterval(VNInterval(i.inf(), i.sup())));
};

template <> 
void HybridExprVisitor<vnodelp::TFinterval>::visited(RealExprRepIntervalConst& e) 
{
	Interval i(e.getInterval());
	VNIntPtr vi(new VNInterval(i.inf(), i.sup()));
	m_value = VISIT_PTR_GEN(vnodelp::TFinterval)(new vnodelp::TFinterval(*vi));
};

template <> 
void HybridExprVisitor<RealExpr>::visited(RealExprRepIntervalConst& e) 
{
	m_value = VISIT_PTR_GEN(RealExpr)(
		new RealExpr(IntervalConstant(e.getInterval())));
};

}

#endif /* __HYBRID_EXPR_VISITOR__ */
#endif /* __cplusplus */
